# ~*~ coding: utf-8 ~*~
from flask import request, views, render_template, jsonify
from flask_login import current_user

from apps.common.utils import fill_date_to_timestamp, timestamp_obj_to_str, list2dict
from apps.common.mixin import CompatibleWithSelect2Mixin
from apps.common import response
from apps.extensions import db
from apps.perms.mixin import LoginRequiredMixin
from apps.users.models import User
from ..models import Project, Environment, Server, Permission, Label, Filter, Task
from ..forms import ProjectForm, PermissionForm
from ..mixin import ProjectPermissionMixin


__all__ = [
    'ProjectListAPI', 'ProjectListView', 'ProjectCreateView',
    'ProjectUpdateView', 'ProjectDeleteView', 'ProjectItemAPI',
    'ProjectPermissionView'
]


class ProjectListAPI(CompatibleWithSelect2Mixin, ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):

    def has_perm(self):
        return True

    def get(self):
        create_time = request.args.get('create_time')
        start_time = end_time = None
        if create_time:
            start_time = fill_date_to_timestamp('start', create_time.split('~')[0])
            end_time = fill_date_to_timestamp('end', create_time.split('~')[1])

        filter_conditions = {
            'and': [
                ('create_time', 'gte', start_time),
                ('create_time', 'lte', end_time),
            ],
            'or': [
                ('name', 'like', request.args.get('search')),
            ],
        }

        # 排序
        sort = request.args.get('sort') or 'create_time'
        order = request.args.get('order') or 'desc'
        order_by_list = [(sort, order), ]

        # 分页
        offset = request.args.get('offset') or 0
        limit = request.args.get('limit') or 12

        # Compatible with select2
        if request.values.get('type') == 'select2':
            filter_conditions, order_by_list, offset, limit = self.select2_queryset()

        projects, count = Project.get_queryset(queryset=self.get_queryset(),
                                               filter_conditions=filter_conditions,
                                               order_by_list=order_by_list,
                                               offset=offset, limit=limit)

        # 关联查询所属环境信息
        project_ids = [project.id for project in projects]
        environment_dict = db.session.query(Project.id, Environment.name).\
            join(Environment, Project.environment_id == Environment.id).\
            filter(Project.id.in_(project_ids)).\
            all()
        environment_dict = list2dict(environment_dict)

        # 关联查询所属授权信息
        user_dict = db.session.query(Permission.project_id, User.name).\
            join(User, Permission.user_id == User.id).\
            filter(Permission.project_id.in_(project_ids)).\
            all()
        user_dict = list2dict(user_dict)

        # 关联查询所属标签信息
        label_dict = db.session.query(Project.id, Label.name). \
            join(Label, Project.label_id == Label.id). \
            filter(Project.id.in_(project_ids)). \
            all()
        label_dict = list2dict(label_dict)

        results = list()
        for project in projects:
            results.append({
                'id': project.id,
                'name': project.name,
                'environment': environment_dict.get(project.id),
                'repo_mode': project.repo_mode,
                'repo_type': project.repo_type,
                'status': project.status,
                'label': label_dict.get(project.id),
                'permission': user_dict.get(project.id),
                'create_by': project.create_by,
                'create_time': timestamp_obj_to_str(project.create_time)
            })

        resp = {
            "data": {
                "rows": results,
                "total": count
            }
        }
        return jsonify(resp)


class ProjectItemAPI(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):

    def has_perm(self):
        return True

    def get(self, pk):
        instance = db.session.query(Project).get(pk)
        content = {
            'id': instance.id,
            'name': instance.name,
            'environment_id': instance.environment_id,
            'repo_url': instance.repo_url,
            'repo_username': instance.repo_username,
            'repo_password': instance.repo_password,
            'repo_mode': instance.repo_mode,
            'repo_type': instance.repo_type,
            'target_root': instance.target_root,
            'target_releases': instance.target_releases,
            'vars': instance.vars,
            'keep_version_num': instance.keep_version_num,
            'prev_deploy': instance.prev_deploy,
            'post_deploy': instance.post_deploy,
            'prev_release': instance.prev_release,
            'post_release': instance.post_release,
            'task_audit': instance.task_audit,
            'status': instance.status,
            'label_id': instance.label_id,
            'privilege_user': instance.privilege_user,
            'describe': instance.describe
        }
        resp = {
            "data": content
        }
        return jsonify(resp)


class ProjectListView(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'codes-project-list'

    def get(self):
        return render_template('codes/project_list.html')


class ProjectUpdateView(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'codes-project-update'

    def get(self, pk):
        instance = db.session.query(Project).get(pk)
        servers = db.session.query(Server).filter(Server.project_id == instance.id).all()
        server_ids = [server.asset_id for server in servers]
        filters = db.session.query(Filter).filter(Filter.project_id == instance.id).order_by(Filter.create_time.desc()).all()
        filter_filers = [ft.filename for ft in filters]
        content = {
            'id': instance.id,
            'name': instance.name,
            'environment_id': instance.environment_id,
            'repo_url': instance.repo_url,
            'repo_username': instance.repo_username,
            'repo_password': instance.repo_password,
            'repo_mode': instance.repo_mode,
            'repo_type': instance.repo_type,
            'target_root': instance.target_root,
            'target_releases': instance.target_releases,
            'vars': instance.vars,
            'keep_version_num': instance.keep_version_num,
            'server_ids': ','.join(server_ids),
            'prev_deploy': instance.prev_deploy,
            'post_deploy': instance.post_deploy,
            'prev_release': instance.prev_release,
            'post_release': instance.post_release,
            'task_audit': instance.task_audit,
            'status': instance.status,
            'label_id': instance.label_id,
            'privilege_user': instance.privilege_user,
            'sparse_dir': instance.sparse_dir,
            'describe': instance.describe,
            'filter_filers': '\r\n'.join(filter_filers)
        }
        return render_template('codes/project_update.html', **content)

    def post(self, pk):
        form = ProjectForm(request.values)
        instance = db.session.query(Project).get(pk)
        if form.validate():
            _clean_data = form.data
            if instance.name != _clean_data.get('name'):
                if db.session.query(Project).filter_by(name=_clean_data.get('name')).count():
                    return response.params_error({
                        'field': 'name',
                        'message': '项目名称不能重复'
                    })

            instance.name = _clean_data.get('name')
            instance.environment_id = _clean_data.get('environment_id')
            instance.repo_url = _clean_data.get('repo_url')
            instance.repo_username = _clean_data.get('repo_username')
            instance.repo_password = _clean_data.get('repo_password')
            instance.repo_mode = _clean_data.get('repo_mode')
            instance.repo_type = _clean_data.get('repo_type')
            instance.target_root = _clean_data.get('target_root')
            instance.target_releases = _clean_data.get('target_releases')
            instance.vars = _clean_data.get('vars')
            instance.keep_version_num = _clean_data.get('keep_version_num')
            instance.prev_deploy = _clean_data.get('prev_deploy')
            instance.post_deploy = _clean_data.get('post_deploy')
            instance.prev_release = _clean_data.get('prev_release')
            instance.post_release = _clean_data.get('post_release')
            instance.task_audit = _clean_data.get('task_audit')
            instance.status = _clean_data.get('status')
            instance.label_id = _clean_data.get('label_id')
            instance.privilege_user = _clean_data.get('privilege_user')
            instance.sparse_dir = _clean_data.get('sparse_dir')
            instance.describe = _clean_data.get('describe')
            instance.create_by = current_user.name
            instance.update(commit=False)

            # 项目部署的服务器
            db.session.query(Server).filter(Server.project_id == instance.id).delete()
            server_ids = _clean_data.get('server_ids')
            server_id_list = server_ids.split(',')
            db.session.bulk_insert_mappings(
                Server,
                [dict(project_id=instance.id, asset_id=server_id) for server_id in server_id_list]
            )

            # 过滤的文件
            newly_filter_files = _clean_data.get('filter_files').split('\r\n')
            newly_filter_files = [f.lstrip('.') for f in newly_filter_files]
            newly_filter_files = [f.rstrip('/').lstrip('/') for f in newly_filter_files]
            newly_filter_files = [f for f in newly_filter_files if f]
            try:
                exist_filter_files = db.session.query(Filter.filename).filter(Filter.project_id == instance.id).all()
                exist_filter_files = list(zip(*exist_filter_files))[0]
            except IndexError:
                exist_filter_files = list()

            needed_del_filter_files = set(exist_filter_files) - set(newly_filter_files)
            db.session.query(Filter).\
                filter(Filter.project_id == instance.id, Filter.filename.in_(needed_del_filter_files)).\
                delete(synchronize_session=False)
            needed_add_filter_files = set(newly_filter_files) - set(exist_filter_files)
            db.session.bulk_insert_mappings(
                Filter,
                [dict(project_id=instance.id,
                      filename=filter_file,
                      create_by=current_user.name) for filter_file in needed_add_filter_files]
            )

            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())


class ProjectCreateView(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'codes-project-create'

    def get(self):
        return render_template('codes/project_create.html')

    def post(self):
        form = ProjectForm(request.values)
        if form.validate():
            _clean_data = form.data
            if db.session.query(Project).filter_by(name=_clean_data.get('name')).count():
                return response.params_error({
                    'field': 'name',
                    'message': '项目名称不能重复'
                })

            project = Project()
            project.name = _clean_data.get('name')
            project.environment_id = _clean_data.get('environment_id')
            project.repo_url = _clean_data.get('repo_url')
            project.repo_username = _clean_data.get('repo_username')
            project.repo_password = _clean_data.get('repo_password')
            project.repo_mode = _clean_data.get('repo_mode')
            project.repo_type = _clean_data.get('repo_type')
            project.target_root = _clean_data.get('target_root')
            project.target_releases = _clean_data.get('target_releases')
            project.vars = _clean_data.get('vars')
            project.keep_version_num = _clean_data.get('keep_version_num')
            project.prev_deploy = _clean_data.get('prev_deploy')
            project.post_deploy = _clean_data.get('post_deploy')
            project.prev_release = _clean_data.get('prev_release')
            project.post_release = _clean_data.get('post_release')
            project.task_audit = _clean_data.get('task_audit')
            project.status = _clean_data.get('status')
            project.label_id = _clean_data.get('label_id')
            project.privilege_user = _clean_data.get('privilege_user')
            project.sparse_dir = _clean_data.get('sparse_dir')
            project.describe = _clean_data.get('describe')
            project.create_by = current_user.name
            db.session.add(project)
            db.session.flush()

            # 项目部署的服务器
            server_ids = _clean_data.get('server_ids')
            server_id_list = server_ids.split(',')
            db.session.bulk_insert_mappings(
                Server,
                [dict(project_id=project.id, asset_id=server_id) for server_id in server_id_list]
            )

            # 过滤的文件
            filter_file_list = _clean_data.get('filter_files').split('\r\n')
            filter_file_list = [f.lstrip('.') for f in filter_file_list]
            filter_file_list = [f.rstrip('/').lstrip('/') for f in filter_file_list]
            filter_file_list = [f for f in filter_file_list if f]
            db.session.bulk_insert_mappings(
                Filter,
                [dict(project_id=project.id, filename=f, create_by=current_user.name) for f in filter_file_list]
            )

            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())


class ProjectDeleteView(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'codes-project-delete'

    def post(self, pk):
        instance = db.session.query(Project).get(pk)
        instance.delete(commit=False)
        db.session.query(Server).filter(Server.project_id == instance.id).delete(synchronize_session=False)
        db.session.query(Permission).filter(Permission.project_id == instance.id).delete(synchronize_session=False)
        db.session.query(Filter).filter(Filter.project_id == instance.id).delete(synchronize_session=False)
        db.session.query(Task).filter(Task.project_id == instance.id).delete(synchronize_session=False)
        db.session.commit()
        return response.success()


class ProjectPermissionView(ProjectPermissionMixin, LoginRequiredMixin, views.MethodView):
    permission_required = 'codes-project-permission'

    def get(self, pk):
        instance = db.session.query(Project).get(pk)
        permissions = db.session.query(Permission).filter(Permission.project_id == instance.id).all()
        user_ids = [permission.user_id for permission in permissions]

        content = {
            'id': instance.id,
            'name': instance.name,
            'user_ids': ','.join(user_ids)
        }
        return render_template('codes/project_permission.html', **content)

    def post(self, pk):
        form = PermissionForm(request.values)
        if form.validate():
            _clean_data = form.data
            project_id = _clean_data.get('project_id')
            user_ids = _clean_data.get('user_ids').split(',')

            db.session.query(Permission).filter(Permission.project_id == project_id).delete(synchronize_session=False)
            db.session.bulk_insert_mappings(
                Permission,
                [dict(project_id=project_id, user_id=user_id) for user_id in user_ids]
            )
            db.session.commit()
            return response.success()
        else:
            return response.params_error(form.get_error())